using System;
using System.Collections;


namespace SolarDevices
{
    public class TriStarRegisterTable : RegisterTableBase, IRegisterTable
    {
        protected readonly Register[] _chargeStateRegister;
        private IModbusDevice _modbusDevice;

        public TriStarRegisterTable(IModbusDevice modbusDevice)
        {
            _chargeStateRegister = new[]{
                                           new Register { Address = 1, Label = "V Scale", UnitOfMeasure = "", Conversion = address => U32(address)},
                                           new Register { Address = 3, Label = "A Scale", UnitOfMeasure = "", Conversion = address => U32(address)},
                                           new Register { Address = 25, Label = "Average battery voltage", UnitOfMeasure = "Volts", Conversion = address => VScale(address)},
                                           new Register { Address = 28, Label = "PV input voltage", UnitOfMeasure = "Volts", Conversion = address => VScale(address)},
                                           new Register { Address = 29, Label = "Average battery current", UnitOfMeasure = "Amps", Conversion = address => IScale(address)},
                                           new Register { Address = 30, Label = "Average PV current", UnitOfMeasure = "Amps", Conversion = address => IScale(address)},
                                           new Register { Address = 45, Label = "Info flag", UnitOfMeasure = "", Conversion = address => Info(address)},
                                           new Register { Address = 51, Label = "Battery charge state", UnitOfMeasure = "", Conversion = address => ChargeState(address)},
                                           new Register { Address = 58, Label = "Total kWhours", UnitOfMeasure = "kWh", Conversion = address => U16(address)},
                                           new Register { Address = 59, Label = "Average power to the battery", UnitOfMeasure = "Watts", Conversion = address => PScale(address)},
                                           new Register { Address = 69, Label = "Average energy to the battery", UnitOfMeasure = "kWh", Conversion = address => WHr(address)}

                                       };

            _offset = _chargeStateRegister[0].Address - 1;
            _modbusDevice = modbusDevice;
            _modbusDevice.Address = _offset;
            _modbusDevice.Count = _chargeStateRegister[_chargeStateRegister.Length - 1].Address - _offset + 4;

        }

        public void Dispose()
        {
            if (_modbusDevice != null)
            {
                var disp = (IDisposable) _modbusDevice;
                disp.Dispose();
            }
            _modbusDevice = null;
        }

        public void StartPolling()
        {
            _modbusDevice.StartPolling();
        }

        public void AddInverterData(ref ChargeState chargeState)
        {
            return; // not implemented
        }

        public void ReadChargeEvent(ref ChargeEvent ce)
        {
            if (_modbusDevice.Connected == false)
            {
                throw new Exception("No connection");
            }
            var code = Data[51 - Offset];
            switch (code)
            {

                case 3:
                    ce.StateCode = StateCode.Resting;
                    break;
                case 5:
                    ce.StateCode = StateCode.Bulk;
                    break;
                case 6:
                    ce.StateCode = StateCode.Absorb;
                    break;
                case 7:
                    ce.StateCode = StateCode.Float;
                    break;
                case 8:
                    ce.StateCode = StateCode.Equalize;
                    break;
                default:
                    ce.StateCode = StateCode.Resting;
                    break;

            }
            ce.EnergyToday = WHr(68);
        }

        public void ReadInverterEvent(ref ChargeEvent ce)
        {
            return; // not implemented
        }

        public ChargeState ReadChargeState()
        {
            if (!_modbusDevice.Connected)
            {
                var chargeState = new ChargeState
                {
                    Connected = false
                };
                return chargeState;
            }
            _data = _modbusDevice.GetData();
            if (_data != null)
            {
                var chargeState = new ChargeState
                {
                    BatteryVolts = _chargeStateRegister[2].Text,
                    PvVolts = _chargeStateRegister[3].Text,
                    BatteryAmps = _chargeStateRegister[4].Text,
                    EnergyToday = _chargeStateRegister[10].Text,
                    Watts = _chargeStateRegister[9].Text,
                    State = _chargeStateRegister[7].Text,
                    PvAmps = _chargeStateRegister[5].Text,
                    TotalKwH = _chargeStateRegister[8].Text,
                    ControllerError = _chargeStateRegister[6].Text,
                    Connected = true
                };
                return chargeState;
            }
            return new ChargeState();
        }

        private string Info(int address)
        {
            return TriStarLookupTables.ParseInfoFlag(Data[address - Offset]);
        }

        private string ChargeState(int address)
        {
            return TriStarLookupTables.LookupBatteryStage(Data[address - Offset]);
        }
        
        protected string WHr(int address)
        {
            var val = (double)Data[address - Offset];
            val /= 1000;
            return val.ToString("f1");
        }


        protected string PScale(int address)
        {
            var val = (double)Data[address - Offset];
            val = val * V_PU * I_PU;
            val /= 131072;
            return val.ToString("f1");
        }

        protected string VScale(int address)
        {
            var val = (double)Data[address - Offset];
            val *= V_PU;
            val /= 32768;
            return val.ToString("f1");
        }

        protected string IScale(int address)
        {
            var val = (double)Data[address - Offset];
            val *= I_PU;
            val /= 32768;
            return val.ToString("f1");
        }


        protected double V_PU
        {
            get
            {
                var hi = (double) Data[0];
                var lo = (double) Data[1];
                lo = lo/65536;
                return hi + lo;
            }
        }

        protected double I_PU
        {
            get
            {
                var hi = (double) Data[2];
                var lo = (double) Data[3];
                lo = lo/65536;
                return hi + lo;
            }
        }
    }
}